#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <iostream>
#include <mili/mili.h>
#include "prot-filer/xtc_filer.h"
#include "prot-filer/cached_reader.h"
#include "feca/feca.h"

using namespace std;
using namespace prot_filer;
using mili::insert_into;

namespace prot_filer
{
ostream& operator<<(ostream& os, const Coord3d& c)
{
    return os << "(" << c.x << ", " << c.y << ", " << c.z << ")";
}
}

bool eq_coords(Coord3d c1, Coord3d c2)
{
    return c1.x == c2.x && c1.y == c2.y && c1.z == c1.z;
}

class XtcFilerTest: public ::testing::Test
{
protected:
    virtual void SetUp()
    {
        insert_into(data, Coord3d(1, 2, 3));
        insert_into(data, Coord3d(3, 1, 3));
        insert_into(data, Coord3d(9.6, 1, 20));
        insert_into(data, Coord3d(1, 2, 3));
        insert_into(data, Coord3d(1, 2, 3));
        insert_into(data, Coord3d(2, 5, 3));
    }

    virtual void TearDown()
    {
        Coord3DReaderFactory::destroy_instance();
    }

    vector<Coord3d> data;
    void create_as_basic_protein(const string& file);
    void create_as_protein(const string& file);
    void read_as_protein(const string& file);
    void read_as_basic_protein(const string& file);
};

TEST_F(XtcFilerTest, create_as_basic_read_as_protein)
{
    const string file = "test.xtc";
    create_as_basic_protein(file);
    read_as_protein(file);
    remove(file.c_str());
}

TEST_F(XtcFilerTest, create_as_basic_read_as_basic)
{
    const string file = "test.xtc";
    create_as_basic_protein(file);
    read_as_basic_protein(file);
    remove(file.c_str());
}

TEST_F(XtcFilerTest, create_as_protein_read_as_protein)
{
    const string file = "test.xtc";
    create_as_protein(file);
    read_as_protein(file);
    remove(file.c_str());
}

TEST_F(XtcFilerTest, create_as_protein_read_as_basic)
{
    const string file = "test.xtc";
    create_as_protein(file);
    read_as_basic_protein(file);
    remove(file.c_str());
}

void XtcFilerTest::create_as_basic_protein(const string& file)
{
    Protein protein;
    for (vector<Coord3d>::iterator it = data.begin(); it != data.end(); ++it)
    {
        protein.addItem(*it);
    }
    XtcWriter writer;
    writer.open(file);
    writer.write(protein);
    writer.write(protein);
    writer.close();
}

void XtcFilerTest::create_as_protein(const string& file)
{
    BasicProtein protein(data.size());
    for (unsigned int atom_number = 0; atom_number < data.size(); ++atom_number)
    {
        protein[atom_number].x = data[atom_number].x * 10;
        protein[atom_number].y = data[atom_number].y * 10;
        protein[atom_number].z = data[atom_number].z * 10;
    }
    XtcWriter writer;
    AnglesMapping mapping(data.size() / 3);
    AnglesData angles_data(data.size() / 3, &mapping);
    writer.open(file);
    writer.write(protein, angles_data);
    writer.write(protein, angles_data);
    writer.close();
}

void XtcFilerTest::read_as_protein(const string& file)
{
    CachedReader<FullCache, Coord3DReader, Protein> r(Coord3DReaderFactory::get_instance()->create("xtc"));
    r.open(file);
    unsigned int i = 0;
    Protein* protein = NULL;
    while ((protein = r.read(i)) != NULL)
    {
        ASSERT_EQ(data.size(), protein->items());
        for (unsigned int atom_number = 0; atom_number < protein->items(); ++atom_number)
        {
            ASSERT_PRED2(eq_coords, data[atom_number], (*protein)[atom_number]);
        }
        ++i;
    }
    ASSERT_EQ(2, i);
    r.close();
}

void XtcFilerTest::read_as_basic_protein(const string& file)
{
    CachedReader<FullCache, Coord3DReader, BasicProtein> r(Coord3DReaderFactory::get_instance()->create("xtc"));
    r.open(file);
    unsigned int i = 0;
    BasicProtein* protein = NULL;
    while ((protein = r.read(i)) != NULL)
    {
        for (unsigned int atom_number = 0; atom_number < data.size(); ++atom_number)
        {
            Coord3d c10 = data[atom_number];
            c10 /= .1; //coords x10
            ASSERT_PRED2(eq_coords, c10, (*protein)[atom_number]);
        }
        ++i;
    }
    ASSERT_EQ(2, i);
    r.close();
}
